#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <sys/ioctl.h>
#include <poll.h>
#include <signal.h>

#include "./include/common.h"

#define BUFSIZE        (32) /* 设置最大偏移量为64, 方便打印完整的内存空间数据*/

/**
 * @brief  usage_info()
 * @note   打印帮助信息
 * @param [in]
 * @param [out]
 * @retval 
 */
void usage_info(void)
{
	printf("\n");
	printf("+++++++++++++++++++++++++++++++++++++++++\n");
	printf("+       help information  @sumu          +\n");
	printf("+++++++++++++++++++++++++++++++++++++++++\n");

	printf("help:\n");
	printf("use format: ./app_name /dev/device_name arg1 ... \n");
	printf("            ./app_demo.out /dev/sdevice 1 x x    # 从x位置读取x字节 \n");
    printf("            驱动中buf最大为32字节 \n");
	printf("\n");

	printf("command info:\n");
	printf("  (1)load module  : insmod module_name.ko\n");
	printf("  (2)unload module: rmmod module_name.ko\n");
	printf("  (3)show module  : lsmod\n");
	printf("  (4)view device  : cat /proc/devices\n");
	printf("  (5)create device node: mknod /dev/device_name c major_num secondary_num \n");
	printf("  (6)show device node  : ls /dev/device_name \n");
	printf("  (7)show device vlass  : ls /sys/class \n");
	printf("+++++++++++++++++++++++++++++++++++++++++\n");
}

void arg_info_print(int argc, char *argv[])
{
    int i = 0;
    PRT("total arg is %d: ", argc);
    for (i = 0; i < argc; i++) 
    {
        printf("argv[%d]=%s ", i, argv[i]);
    }
    printf("\n");
}

// ./xxx.out /dev/sdevice 1 x x
static int func_demo1(int argc, char *argv[])
{
    int read_fd = -1;
    int ret = 0;
    unsigned int i = 0;
    char *dev_name = NULL;
    unsigned int off_end = 0; // 定义读写偏移位置
    unsigned int offset = 0;
    unsigned int read_size = 0;
    char readbuf[BUFSIZE] = {0};

    // 获取设备节点名称
    if(argv[1] == NULL)
    {
        return -1;
    }
    dev_name = argv[1]; // 设备节点名称

    /** 打开设备文件 */
    read_fd = open(dev_name,  O_RDWR); // 使用阻塞标志
    if (read_fd < 0)
    {
        PRTE("can't open file %s !\n", dev_name);
        return -1;
    }

    off_end = lseek(read_fd, 0, SEEK_END); // 读取字符设备文件可读写最大的大小
    offset = atoi(argv[3]);// 获取读写的偏移位置
    if(offset > off_end)
    {
        offset = off_end;
    }

    read_size = atoi(argv[4]);// 获取要读取的字节数
    if((read_size + offset > off_end) || (read_size == 0))
    {
        read_size = off_end - offset;
    }
    //PRT("%s max buffer size is %d, offset=%d, read_size=%d\n", dev_name, off_end, offset, read_size);
    do 
    {
        ret = lseek(read_fd, offset, SEEK_SET); // 将偏移量设置为距离起始地址 off的位置
        if (ret < 0)
        {
            PRTE("lseek failed!\n");
            return ret;;
        }
        memset(readbuf, 0, sizeof(readbuf));
        ret = read(read_fd, readbuf, read_size);
        if (ret < 0)
        {
            PRTE("read from %d failed!\n", read_fd);
        }
        else
        {
            /*  读取成功，打印出读取成功的数据 */
            PRT("read data from %s!", dev_name);
            for(i = 0; i < read_size; i++)
            {
                printf("0x%x ", readbuf[i]);
            }
            printf("\n");
        }
    } while(0);

    /* 关闭设备 */
    ret = close(read_fd);
    if (ret < 0)
    {
        PRTE("can't close file %s !\n", dev_name);
        return -1;
    }

    return 0;
}

int main(int argc, char *argv[])
{
    unsigned int arg1 = 0;
    
    printf("*** Build Time: %s %s,Git Version: %s Git Remote: %s***\n", 
            __DATE__, __TIME__, GIT_VERSION, GIT_PATH);
    /** 
     * 获取传入参数并打印 
     * ./xxx.out /dev/sdevice x x x
     */
    arg_info_print(argc, argv);
    if (argc < 3) 
    {
        usage_info();
        return -1;
    }

    /** 解析参数 */
    arg1 = atoi(argv[2]);  // 1=读，2=写，3=ioctl

    if (arg1 == 1) /** 从设备节点指定位置读取数据 */
    {
        func_demo1(argc, argv);
    }

    return 0;
}

/**
 * @brief  sys_pts_get_ms
 * @note   获取系统时间戳 单位 ms
 * @param [in]
 * @param [out]
 * @retval 
 */
int sys_pts_get_ms(void)
{
    struct timespec tv = {0};
    long long lasttime = 0;

    clock_gettime(CLOCK_MONOTONIC, &tv);
    lasttime = tv.tv_sec * 1000 + tv.tv_nsec / (1000 * 1000);

    return lasttime;

}